package apibasej.db.server.pool;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.sql.DataSource;

import apibasej.basic.pool.APIBasePoolException;
import apibasej.basic.pool.SimplePool;

public class SimpleConnectionPool extends SimplePool<PooledConnection> implements DataSource{

	// algumas funcionalidades foram baseadas no DBCP da jakarta
	
	//Internal constant to indicate the level is not set.
    static final int UNKNOWN_TRANSACTIONISOLATION = -1;
    
    //protected Collection _connectionInitSqls = null;
    private String validationQuery = null;// implementar !!! não está sendo usada
    private int validationQueryTimeout = -1;
    private Boolean defaultReadOnly = null;
    private boolean defaultAutoCommit = true;
    private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
    private String defaultCatalog;
    
    
	private String url;
	private String user;
	private String pass;
	
	private String queryValidationConnection;
	
	// não esquecer de registrar os drivers antes!
	public SimpleConnectionPool(String url, String user, String pass) {
		super(5, 100, 200);
		this.url = url;
		this.user = user;
		this.pass = pass;
	}
	public SimpleConnectionPool(String url, String user, String pass, String queryValidationConnection, int initialSize, int recommendedSize, int maxSize) {
		super(initialSize, recommendedSize, maxSize);
		this.url = url;
		this.user = user;
		this.pass = pass;
		this.queryValidationConnection = queryValidationConnection;
	}
	
	
	
	public String getUrl() {
		return url;
	}
	public String getUser() {
		return user;
	}
	public String getPass() {
		return pass;
	}
	public void setQueryValidationConnection(String queryValidationConnection) {
		this.queryValidationConnection = queryValidationConnection;
	}
	public String getQueryValidationConnection() {
		return queryValidationConnection;
	}
	
	
	@Override
	protected PooledConnection newItemPool() throws APIBasePoolException {
		PooledConnection c = new PooledConnection(this);
		if(getQueryValidationConnection()!=null){
			c.setQueryValidationConnection(getQueryValidationConnection());
		}
		return c;
	}
	
	private PrintWriter logWriter;
	@Override
	public PrintWriter getLogWriter() throws SQLException {
		if(logWriter==null) logWriter = new PrintWriter(System.out);
		return logWriter;
	}
	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		this.logWriter = out;
	}
	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		throw new UnsupportedOperationException("Not supported by "+getClass().getName());
	}
	@Override
	public int getLoginTimeout() throws SQLException {
		throw new UnsupportedOperationException("Not supported by "+getClass().getName());
	}
	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		 throw new SQLException(getClass().getName()+" is not a wrapper.");
	}
	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return false;
	}

	
	//private long lastValidation = 0;
	
	@Override
	public Connection getConnection() throws SQLException {
		//Tentar várias vezes! Se der erro em uma é provável que todas estejam inválidas, ou que tenham expirado, por isso deve tentar poolSize+1
		Throwable e = new SQLException("Unexpected error while fetching valid connection from pool"); // não deve ser lançado, pois será sobstuido se ocorrer algum erro na obtenção da conexão
		int tt = getSize()+2; // +2 para caso todas conexões estejam expiradas, deverá invalidar todas e pegar a próxima criada ou a próxima por segurança
		PooledConnection c = null;
		for(int i=0; i<tt; i++){ // tenta somente durante o tamanho do pool, para se tiver algum problema em todas não ficar tentando para sempre
			try{
				c = getItemPool();
				c.activate();
				
				// pode continuar dando o erro no postgre???
				c.validadeConnection(); //por enquanto validar SEMPRE antes de usar ... depois ve ropções para validação separada ou de tempo em tempo ...
				
				/*//para não validar toda hora ...
				if((System.currentTimeMillis()-lastValidation)>(1*60*1000)){ // !!! ver melhor este tempo! ter opção de setar???
					c.validadeConnection();
					lastValidation = System.currentTimeMillis();
				}*/
				// validar com a query a cada chamada??? 
				// melhor ter uma rotina separada que de tempo em tempo chama o c.verifyInvalidateConnection() para cada conn do pool
				// pode ser útil também para manter as conexões ativas 
				
				return c;
			}catch (Throwable e1) {
				if(c!=null){
					c.invalidate();
				}
				e = e1;
				logDef(Level.SEVERE,"Error on activate connection. A new attempt will be made in the pool. Try "+(i+1)+" of "+tt+", PoolSize="+getSize()+", InfoConn="+(c!=null?c.getInfo():"NULL"),e1);
			}
		}
		if(e instanceof SQLException){
			throw (SQLException)e;
		}else{
			throw new SQLException("Error on get and activate connection from pool. Attempts="+tt+", InfoConn="+(c!=null?c.getInfo():"NULL"),e);
		}
		/*
		PooledConnection c = null;
		try{
			c = getItemPool();
			c.activate();
			return c;
		}catch (Throwable e1){
			if(c!=null) {
				c.invalidate();
			}
			String m = "Error on get and activate connection from pool";
			logSysOut(ERROR_ON_ACTIVATE_CONNECTION,m,e1);//?? como já lança a exceção não registrar log aqui??? se o logger tiver alguma operação de banco irá entrar em loop infinito !!! ver se pode ocorrer para outros pontos de log!!!
			throw new SQLException(m,e1);
		}*/
	}
	
	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		// se precisar pode ir criando um pool para cada usuario e senha ...
		throw new UnsupportedOperationException("Not supported by "+getClass().getName());
	}
	
	//*******************************************************************
	
	
	public String getValidationQuery() {
		return validationQuery;
	}
	public int getValidationQueryTimeout() {
		return validationQueryTimeout;
	}
	public Boolean getDefaultReadOnly() {
		return defaultReadOnly;
	}
	public boolean isDefaultAutoCommit() {
		return defaultAutoCommit;
	}
	public int getDefaultTransactionIsolation() {
		return defaultTransactionIsolation;
	}
	public String getDefaultCatalog() {
		return defaultCatalog;
	}
	public void setValidationQuery(String validationQuery) {
		this.validationQuery = validationQuery;
	}
	public void setValidationQueryTimeout(int validationQueryTimeout) {
		this.validationQueryTimeout = validationQueryTimeout;
	}
	public void setDefaultReadOnly(Boolean defaultReadOnly) {
		this.defaultReadOnly = defaultReadOnly;
	}
	public void setDefaultAutoCommit(boolean defaultAutoCommit) {
		this.defaultAutoCommit = defaultAutoCommit;
	}
	public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
		this.defaultTransactionIsolation = defaultTransactionIsolation;
	}
	public void setDefaultCatalog(String defaultCatalog) {
		this.defaultCatalog = defaultCatalog;
	}
	
	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		return Logger.getLogger(getClass().getPackage().getName());
	}

}
